package mis.ds.ps.controller;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;

import mis.ds.ps.model.Mapper;
import mis.ds.ps.model.MapperLeaf;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Sheet;

public class MapperExecutor {

	private static Logger logger = Logger.getLogger(MapperExecutor.class);

	private String fileToImport;
	private InputStream inputStream;
	private HSSFWorkbook workBook;
	private Mapper mapper;
	private Result result;

	public MapperExecutor(String fileToImport) {

		PropertyConfigurator.configure("log4j.properties");

		this.fileToImport = fileToImport;

		if (fileToImport != null) {
			this.inputStream = getInputStreamFromFile(fileToImport);
		}

		if (inputStream != null) {
			this.setWorkBook(getWorkBookFromInput(inputStream));
		}
	}

	public MapperExecutor(String fileToImport, Mapper mapper) {
		super();

		PropertyConfigurator.configure("log4j.properties");

		this.fileToImport = fileToImport;

		if (fileToImport != null) {
			this.inputStream = getInputStreamFromFile(fileToImport);
		}

		if (inputStream != null) {
			this.setWorkBook(getWorkBookFromInput(inputStream));
		}

		this.mapper = mapper;
	}

	public List<Sheet> getFileSheets() {

		List<Sheet> sheetList = new LinkedList<Sheet>();

		for (int i = 0; i < 100; i++) {
			try {
				Sheet sheet = workBook.getSheetAt(i);
				if (sheet != null) {
					sheetList.add(sheet);
				} else {
					break;
				}
			} catch (IllegalArgumentException e) {
				break;
			}

		}

		return sheetList;
	}

	public Result read() {

		// Mapper must have startCell, stopCell and SheetId

		if (!mapper.isReadyForExecute()) {
			return null;
		}

		MapperLeaf ml = (MapperLeaf) mapper;
		Sheet sheet = workBook.getSheet(ml.getSheetName());
		// Sheet sheet = workBook.getSheetAt(ml.getSheet());

		if (ml.isValid()) {

			result = new Result(ml.getEntity().toString());

			// Same row and column
			if (ml.getStartCell().getRow() == ml.getStopCell().getRow() && ml.getStopCell().getRow() == ml.getStopCell().getCol()) {

				Cell cell = sheet.getRow(ml.getStartCell().getRow()).getCell(ml.getStartCell().getCol());
				readCell(ml.getAttribute(), cell, ml.isAcceptDuplicate());

				// Same row
			} else if (ml.getStartCell().getRow() == ml.getStopCell().getRow()) {
				
				for (int j = ml.getStartCell().getCol(); j < ml.getStopCell().getCol(); j++) {

					// Get Cell
					logger.debug("Getting cell info on [" + String.valueOf(ml.getStartCell().getRow()) + "," + j + "]");
					Cell cell = sheet.getRow(ml.getStartCell().getRow()).getCell(j);
					readCell(ml.getAttribute(), cell, ml.isAcceptDuplicate());
				}

				// Same column

			} else if (ml.getStartCell().getCol() == ml.getStopCell().getCol()) {

				// iterate column indexes

				for (int j = ml.getStartCell().getRow(); j < ml.getStopCell().getRow(); j++) {

					// Get Cell
					logger.debug("Getting cell info on [" + j + "," + ml.getStartCell().getCol() + "]");
					Cell cell = sheet.getRow(j).getCell(ml.getStartCell().getCol());
					readCell(ml.getAttribute(), cell, ml.isAcceptDuplicate());
				}

				// Row and column different

			} else {

				for (int i = ml.getStartCell().getRow(); i < ml.getStopCell().getRow(); i++) {
					for (int j = ml.getStartCell().getCol(); j < ml.getStopCell().getCol(); j++) {

						// Get Cell
						logger.debug("Getting cell info on [" + i + "," + j + "]");
						Cell cell = sheet.getRow(i).getCell(j);
						readCell(ml.getAttribute(), cell, ml.isAcceptDuplicate());
					}

				}
			}
		}

		return result;

	}

	public String getFileToImport() {
		return fileToImport;
	}

	public void setFileToImport(String fileToImport) {
		this.fileToImport = fileToImport;
	}

	public Mapper getMapper() {
		return mapper;
	}

	public void setMapper(Mapper mapper) {
		this.mapper = mapper;
	}

	private InputStream getInputStreamFromFile(String fileToImport) {

		InputStream input = null;
		try {
			input = new FileInputStream(fileToImport);
		} catch (FileNotFoundException e1) {
			logger.debug("File not found ", e1);
		}
		logger.debug("File imported successfully");

		return input;
	}

	private HSSFWorkbook getWorkBookFromInput(InputStream input) {
		HSSFWorkbook wb = null;
		try {
			wb = new HSSFWorkbook(new POIFSFileSystem(input));
		} catch (IOException e) {
			logger.debug("Exception on file inputStream ", e);
			return null;
		}
		logger.debug("Workbook created with sucess ");

		return wb;
	}

	public void setWorkBook(HSSFWorkbook workBook) {
		this.workBook = workBook;
	}

	public HSSFWorkbook getWorkBook() {
		return workBook;
	}

	private void readCell(String att, Cell cell, boolean acceptDuplicated) {

		if (cell != null) {
			switch (cell.getCellType()) {
			case Cell.CELL_TYPE_STRING:

				if (!acceptDuplicated && result.contains(cell.getRichStringCellValue().getString())) {
					break;
				}

				result.addResult(att, cell.getRichStringCellValue().getString());

				break;
			case Cell.CELL_TYPE_NUMERIC:

				if (DateUtil.isCellDateFormatted(cell)) {
					if (!acceptDuplicated && result.contains(cell.getDateCellValue())) {
						break;
					}
					result.addResult(att, cell.getDateCellValue());
				} else {
					if (!acceptDuplicated && result.contains(cell.getNumericCellValue())) {
						break;
					}
					result.addResult(att, cell.getNumericCellValue());
				}
				break;
			case Cell.CELL_TYPE_BOOLEAN:

				if (!acceptDuplicated && result.contains(cell.getBooleanCellValue())) {
					break;
				}

				result.addResult(att, cell.getBooleanCellValue());
				break;
			case Cell.CELL_TYPE_FORMULA:

				if (!acceptDuplicated && result.contains(cell.getCellFormula())) {
					break;
				}
				result.addResult(att, cell.getCellFormula());
				break;
			default:
				break;
			}
		}
	}

	public Result getResult() {
		return result;
	}

}
